package library.net.retrofit.okhttp;

import android.support.annotation.NonNull;

import java.net.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;

import library.net.retrofit.interceptor.RxCacheInterceptor;
import library.net.retrofit.interceptor.RxCommonHeaderInterceptor;
import library.net.retrofit.interceptor.RxHttpLoggingInterceptor;
import library.net.retrofit.interceptor.RxNetCacheInterceptor;
import okhttp3.Cache;
import okhttp3.ConnectionPool;
import okhttp3.CookieJar;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;

/**
 * user-defined OkHttpClient builder
 *
 * @author Created by qiang.hou on 2018/9/5.
 * @version 1.0
 */
public class RxOkHttpBuilder {
    /**
     * time out
     */
    private final static int DEFAULT_READ_TIME_OUT = 10000;
    private final static int DEFAULT_WRITE_TIME_OUT = 10000;
    private final static int DEFAULT_CONNECT_TIME_OUT = 5000;

    /**
     * default interceptor key: log header cache
     */
    public final static String INTERCEPTOR_LOG = "RxHttpLoggingInterceptor";
    public final static String INTERCEPTOR_COMMON_HEADER = "RxCommonHeaderInterceptor";
    public final static String INTERCEPTOR_CACHE_USER_DEFINED = "RxCacheInterceptor";
    public final static String INTERCEPTOR_NET_CACHE_USER_DEFINED = "RxNetCacheInterceptor";

    /**
     * user-defined interceptor key: common params
     */
    public final static String INTERCEPTOR_COMMON_PARAMS = "RxCommonParamsInterceptor";

    private TimeUnit unit = TimeUnit.MILLISECONDS;
    private long readTimeout = DEFAULT_READ_TIME_OUT;
    private long writeTimeout = DEFAULT_WRITE_TIME_OUT;
    private long connectTimeout = DEFAULT_CONNECT_TIME_OUT;

    private Cache cache;
    private Proxy proxy = null;
    private CookieJar cookieJar;
    private X509TrustManager trustManager;
    private SSLSocketFactory sslSocketFactory;
    private HostnameVerifier hostnameVerifier;
    private ConnectionPool connectionPool = null;

    private int logLevel = RxHttpLoggingInterceptor.LEVEL.NONE;
    private Map<String, Interceptor> interceptorMap = new HashMap<>();
    private Map<String, Interceptor> netInterceptorMap = new HashMap<>();
    private OkHttpClient.Builder okHttpClientBuilder;

    public RxOkHttpBuilder readTimeout(long readTimeout, TimeUnit unit) {
        this.unit = unit;
        this.readTimeout = readTimeout;
        return this;
    }

    public RxOkHttpBuilder writeTimeout(long writeTimeout, TimeUnit unit) {
        this.writeTimeout = writeTimeout;
        this.unit = unit;
        return this;
    }

    public RxOkHttpBuilder connectTimeout(long connectTimeout, TimeUnit unit) {
        this.connectTimeout = connectTimeout;
        this.unit = unit;
        return this;
    }

    public RxOkHttpBuilder cache(Cache cache) {
        this.cache = cache;
        return this;
    }

    public RxOkHttpBuilder proxy(Proxy proxy) {
        this.proxy = proxy;
        return this;
    }

    public RxOkHttpBuilder cookieJar(CookieJar cookieJar) {
        this.cookieJar = cookieJar;
        return this;
    }

    public RxOkHttpBuilder trustManager(X509TrustManager trustManager) {
        this.trustManager = trustManager;
        return this;
    }

    public RxOkHttpBuilder sslSocketFactory(SSLSocketFactory sslSocketFactory) {
        this.sslSocketFactory = sslSocketFactory;
        return this;
    }

    public RxOkHttpBuilder hostnameVerifier(HostnameVerifier hostnameVerifier) {
        this.hostnameVerifier = hostnameVerifier;
        return this;
    }

    public RxOkHttpBuilder connectionPool(ConnectionPool connectionPool) {
        this.connectionPool = connectionPool;
        return this;
    }

    public RxOkHttpBuilder logLevel(int logLevel) {
        this.logLevel = logLevel;
        return this;
    }

    public RxOkHttpBuilder interceptor(@NonNull Interceptor interceptor) {
        this.interceptorMap.put(interceptor.getClass().getSimpleName(), interceptor);
        return this;
    }

    public RxOkHttpBuilder netInterceptor(@NonNull Interceptor interceptor) {
        this.netInterceptorMap.put(interceptor.getClass().getSimpleName(), interceptor);
        return this;
    }

    public RxOkHttpBuilder interceptorMap(@NonNull Map<String, Interceptor> interceptorMap) {
        this.interceptorMap = interceptorMap;
        return this;
    }

    public RxOkHttpBuilder netInterceptorMap(@NonNull Map<String, Interceptor> netInterceptorMap) {
        this.netInterceptorMap = netInterceptorMap;
        return this;
    }

    public OkHttpClient build() {
        if (readTimeout <= 0) {
            readTimeout = DEFAULT_READ_TIME_OUT;
        }
        if (writeTimeout <= 0) {
            writeTimeout = DEFAULT_WRITE_TIME_OUT;
        }
        if (connectTimeout <= 0) {
            connectTimeout = DEFAULT_CONNECT_TIME_OUT;
        }
        okHttpClientBuilder = new OkHttpClient.Builder()
                .readTimeout(readTimeout, unit)
                .writeTimeout(writeTimeout, unit)
                .connectTimeout(connectTimeout, unit);

        // 设置缓存
        if (null != cache) {
            okHttpClientBuilder.cache(cache);
        }

        // 配置cookie(包含Session)
        if (null != cookieJar) {
            okHttpClientBuilder.cookieJar(cookieJar);
        }

        // 配置Https
        if (null != sslSocketFactory && null != trustManager) {
            okHttpClientBuilder.sslSocketFactory(sslSocketFactory, trustManager);
        }

        // 配置端口
        if (null != hostnameVerifier) {
            okHttpClientBuilder.hostnameVerifier(hostnameVerifier);
        }

        // 配置连接池
        if (null == connectionPool) {
            connectionPool = new ConnectionPool(8, 15, TimeUnit.SECONDS);
        }
        okHttpClientBuilder.connectionPool(connectionPool);

        // 配置代理
        if (null != proxy) {
            okHttpClientBuilder.proxy(proxy);
        }
        // default cache interceptor
        if (!interceptorMap.containsKey(INTERCEPTOR_CACHE_USER_DEFINED)) {
            interceptorMap.put(INTERCEPTOR_CACHE_USER_DEFINED, new RxCacheInterceptor());
        }
        // default net cache interceptor
        if (!netInterceptorMap.containsKey(INTERCEPTOR_NET_CACHE_USER_DEFINED)) {
            netInterceptorMap.put(INTERCEPTOR_NET_CACHE_USER_DEFINED, new RxNetCacheInterceptor());
        }
        // default headers interceptor
        if (!netInterceptorMap.containsKey(INTERCEPTOR_COMMON_HEADER)) {
            netInterceptorMap.put(INTERCEPTOR_COMMON_HEADER, new RxCommonHeaderInterceptor());
        }
        // default log interceptor
        if (!netInterceptorMap.containsKey(INTERCEPTOR_LOG)) {
            netInterceptorMap.put(INTERCEPTOR_LOG, new RxHttpLoggingInterceptor(logLevel));
        }

        for (Map.Entry entry : interceptorMap.entrySet()) {
            okHttpClientBuilder.addInterceptor((Interceptor) entry.getValue());
        }
        for (Map.Entry entry : netInterceptorMap.entrySet()) {
            okHttpClientBuilder.addNetworkInterceptor((Interceptor) entry.getValue());
        }

        return okHttpClientBuilder.retryOnConnectionFailure(true).build();
    }
}
